1 /**
2  * AJAX Upload ( http://valums.com/ajax-upload/ )
3  * Copyright (c) Andris Valums
4  * Licensed under the MIT license ( http://valums.com/mit-license/ )
5  * Thanks to Gary Haran, David Mark, Corey Burns and others
for contributions
6  */

7 (function () {
8     
/* global window */
9     
/* jslint browser: true, devel: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true */
10     
11     
/**
12      * Wrapper
for FireBug's console.log
13      */

14     function log(){
15         
if (typeof(console) != 'undefined' && typeof(console.log) == 'function'){
16             Array.prototype.unshift.call(arguments,
'[Ajax Upload]');
17             console.log( Array.prototype.
join.call(arguments, ' '));
18         }
19     }
20
21     
/**
22      * Attaches
event to a dom element.
23      * @param {Element} el
24      * @param type
event name
25      * @param fn callback This refers to the passed element
26      */

27     function addEvent(el, type, fn){
28         
if (el.addEventListener) {
29             el.addEventListener(type, fn,
false);
30         }
else if (el.attachEvent) {
31             el.attachEvent(
'on' + type, function(){
32                 fn.call(el);
33             });
34         }
else {
35             
throw new Error('not supported or DOM not loaded');
36         }
37     }
38     
39     
/**
40      * Attaches resize
event to a window, limiting
41      * number of
event fired. Fires only when encounteres
42      * delay of
100 after series of events.
43      *
44      * Some browsers fire
event multiple times when resizing
45      * http://www.quirksmode.org/dom/events/resize.html
46      *
47      * @param fn callback This refers to the passed element
48      */

49     function addResizeEvent(fn){
50         
var timeout;
51                
52         addEvent(window,
'resize', function(){
53             
if (timeout){
54                 clearTimeout(timeout);
55             }
56             timeout = setTimeout(fn,
100);
57         });
58     }
59     
60     
// Needs more testing, will be rewriten for next version
61     
// getOffset function copied from jQuery lib (http://jquery.com/)
62     
if (document.documentElement.getBoundingClientRect){
63         
// Get Offset using getBoundingClientRect
64         
// http://ejohn.org/blog/getboundingclientrect-is-awesome/
65         
var getOffset = function(el){
66             
var box = el.getBoundingClientRect();
67             
var doc = el.ownerDocument;
68             
var body = doc.body;
69             
var docElem = doc.documentElement; // for ie
70             
var clientTop = docElem.clientTop || body.clientTop || 0;
71             
var clientLeft = docElem.clientLeft || body.clientLeft || 0;
72              
73             
// In Internet Explorer 7 getBoundingClientRect property is treated as physical,
74             
// while others are logical. Make all logical, like in IE8.
75             
var zoom = 1;
76             
if (body.getBoundingClientRect) {
77                 
var bound = body.getBoundingClientRect();
78                 zoom = (bound.right - bound.left) / body.clientWidth;
79             }
80             
81             
if (zoom > 1) {
82                 clientTop =
0;
83                 clientLeft =
0;
84             }
85             
86             
var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop, left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
87             
88             
return {
89                 top: top,
90                 left: left
91             };
92         };
93     }
else {
94         
// Get offset adding all offsets
95         
var getOffset = function(el){
96             
var top = 0, left = 0;
97             
do {
98                 top += el.offsetTop ||
0;
99                 left += el.offsetLeft ||
0;
100                 el = el.offsetParent;
101             }
while (el);
102             
103             
return {
104                 left: left,
105                 top: top
106             };
107         };
108     }
109     
110     
/**
111      * Returns left, top, right and bottom properties describing the border-box,
112      *
in pixels, with the top-left relative to the body
113      * @param {Element} el
114      * @
return {Object} Contains left, top, right,bottom
115      */

116     function getBox(el){
117         
var left, right, top, bottom;
118         
var offset = getOffset(el);
119         left = offset.left;
120         top = offset.top;
121         
122         right = left + el.offsetWidth;
123         bottom = top + el.offsetHeight;
124         
125         
return {
126             left: left,
127             right: right,
128             top: top,
129             bottom: bottom
130         };
131     }
132     
133     
/**
134      * Helper that takes
object literal
135      * and
add all properties to element.style
136      * @param {Element} el
137      * @param {Object} styles
138      */

139     function addStyles(el, styles){
140         
for (var name in styles) {
141             
if (styles.hasOwnProperty(name)) {
142                 el.style[name] = styles[name];
143             }
144         }
145     }
146         
147     
/**
148      * Function places an absolutely positioned
149      * element
on top of the specified element
150      * copying position and dimentions.
151      * @param {Element}
from
152      * @param {Element} to
153      */

154     function copyLayout(
from, to){
155         
var box = getBox(from);
156         
157         addStyles(to, {
158             position:
'absolute',
159             left : box.left +
'px',
160             top : box.top +
'px',
161             width :
from.offsetWidth + 'px',
162             height :
from.offsetHeight + 'px'
163         });
164     }
165
166     
/**
167     * Creates and returns element
from html chunk
168     * Uses innerHTML to create an element
169     */

170     
var toElement = (function(){
171         
var div = document.createElement('div');
172         
return function(html){
173             div.innerHTML = html;
174             
var el = div.firstChild;
175             
return div.removeChild(el);
176         };
177     })();
178             
179     
/**
180      * Function generates unique id
181      * @
return unique id
182      */

183     
var getUID = (function(){
184         
var id = 0;
185         
return function(){
186             
return 'ValumsAjaxUpload' + id++;
187         };
188     })();
189  
190     
/**
191      * Get file name
from path
192      * @param {String} file path to file
193      * @
return filename
194      */

195     function fileFromPath(file){
196         
return file.replace(/.*(\/|\\)/, "");
197     }
198     
199     
/**
200      * Get file extension lowercase
201      * @param {String} file name
202      * @
return file extenstion
203      */

204     function getExt(file){
205         
return (-1 !== file.indexOf('.')) ? file.replace(/.*[.]/, '') : '';
206     }
207
208     function hasClass(el, name){
209         
var re = new RegExp('\\b' + name + '\\b');
210         
return re.test(el.className);
211     }
212     function addClass(el, name){
213         
if ( ! hasClass(el, name)){
214             el.className +=
' ' + name;
215         }
216     }
217     function removeClass(el, name){
218         
var re = new RegExp('\\b' + name + '\\b');
219         el.className = el.className.replace(re,
'');
220     }
221     
222     function removeNode(el){
223         el.parentNode.removeChild(el);
224     }
225
226     
/**
227      * Easy styling and uploading
228      * @constructor
229      * @param button An element you want convert to
230      * upload button. Tested dimentions up to 500x500px
231      * @param {Object} options See defaults below.
232      */
233     window.AjaxUpload = function(button, options){
234         
this._settings = {
235             // Location of the server-side upload script
236             action:
'upload.php',
237             // File upload name
238             name:
'userfile',
239             // Additional data to send
240             data: {},
241             // Submit file
as soon as it's selected
242             autoSubmit:
true,
243             // The type of data that you
're expecting back from the server.
244             // html and xml are detected automatically.
245             // Only useful
when you are using json data as a response.
246             // Set to
"json" in that case.
247             responseType:
false,
248             // Class applied to button
when mouse is hovered
249             hoverClass:
'hover',
250             // Class applied to button
when AU is disabled
251             disabledClass:
'disabled',
252             // When user selects a file, useful with autoSubmit disabled
253             // You can
return false to cancel upload
254             onChange: function(file, extension){
255             },
256             // Callback to fire before file
is uploaded
257             // You can
return false to cancel upload
258             onSubmit: function(file, extension){
259             },
260             // Fired
when file upload is completed
261             // WARNING! DO NOT USE
"FALSE" STRING AS A RESPONSE!
262             onComplete: function(file, response){
263             }
264         };
265                         
266         // Merge the users options with our defaults
267         
for (var i in options) {
268             
if (options.hasOwnProperty(i)){
269                 
this._settings[i] = options[i];
270             }
271         }
272                 
273         // button isn
't necessary a dom element
274         
if (button.jquery){
275             // jQuery
object was passed
276             button = button[
0];
277         }
else if (typeof button == "string") {
278             
if (/^#.*/.test(button)){
279                 
// If jQuery user passes #elementId don't break it
280                 button = button.slice(
1);
281             }
282             
283             button = document.getElementById(button);
284         }
285         
286         
if ( ! button || button.nodeType !== 1){
287             
throw new Error("Please make sure that you're passing a valid element");
288         }
289                 
290         
if ( button.nodeName.toUpperCase() == 'A'){
291             
// disable link
292             addEvent(button,
'click', function(e){
293                 
if (e && e.preventDefault){
294                     e.preventDefault();
295                 }
else if (window.event){
296                     window.
event.returnValue = false;
297                 }
298             });
299         }
300                     
301         
// DOM element
302         
this._button = button;
303         
// DOM element
304         
this._input = null;
305         
// If disabled clicking on button won't do anything
306         
this._disabled = false;
307         
308         
// if the button was disabled before refresh if will remain
309         
// disabled in FireFox, let's fix it
310         
this.enable();
311         
312         
this._rerouteClicks();
313     };
314     
315     
// assigning methods to our class
316     
AjaxUpload.prototype = {
317         setData: function(data){
318             
this._settings.data = data;
319         },
320         disable: function(){
321             addClass(
this._button, this._settings.disabledClass);
322             
this._disabled = true;
323             
324             
var nodeName = this._button.nodeName.toUpperCase();
325             
if (nodeName == 'INPUT' || nodeName == 'BUTTON'){
326                 
this._button.setAttribute('disabled', 'disabled');
327             }
328             
329             
// hide input
330             
if (this._input){
331                 
// We use visibility instead of display to fix problem with Safari 4
332                 
// The problem is that the value of input doesn't change if it
333                 
// has display none when user selects a file
334                 
this._input.parentNode.style.visibility = 'hidden';
335             }
336         },
337         enable: function(){
338             removeClass(
this._button, this._settings.disabledClass);
339             
this._button.removeAttribute('disabled');
340             
this._disabled = false;
341             
342         },
343         
/**
344          * Creates invisible file input
345          * that will hover above the button
346          * <div><input type=
'file' /></div>
347          */

348         _createInput: function(){
349             
var self = this;
350                         
351             
var input = document.createElement("input");
352             input.setAttribute(
'type', 'file');
353             input.setAttribute(
'name', this._settings.name);
354             
355             addStyles(input, {
356                 
'position' : 'absolute',
357                 
// in Opera only 'browse' button
358                 
// is clickable and it is located at
359                 
// the right side of the input
360                 
'right' : 0,
361                 
'margin' : 0,
362                 
'padding' : 0,
363                 
'width' : '300px',
364                 
'height' : '300px',
365                 
'cursor' : 'pointer'
366             });
367
368             
var div = document.createElement("div");
369             addStyles(div, {
370                 
'display' : 'block',
371                 
'position' : 'absolute',
372                 
'overflow' : 'hidden',
373                 
'margin' : 0,
374                 
'padding' : 0,
375                 
'opacity' : 0,
376                 
// Make sure browse button is in the right side
377                 
// in Internet Explorer
378                 
'direction' : 'ltr',
379                 
//Max zIndex supported by Opera 9.0-9.2
380                 
'zIndex': 2147483583
381             });
382             
383             
// Make sure that element opacity exists.
384             
// Otherwise use IE filter
385             
if ( div.style.opacity !== "0") {
386                 
if (typeof(div.filters) == 'undefined'){
387                     
throw new Error('Opacity not supported by the browser');
388                 }
389                 div.style.filter =
"alpha(opacity=0)";
390             }
391             
392             addEvent(input,
'change', function(){
393                  
394                 
if ( ! input || input.value === ''){
395                     
return;
396                 }
397                             
398                 
// Get filename from input, required
399                 
// as some browsers have path instead of it
400                 
var file = fileFromPath(input.value);
401                                 
402                 
if (false === self._settings.onChange.call(self, file, getExt(file))){
403                     self._clearInput();
404                     
return;
405                 }
406                 
407                 
// Submit form when value is changed
408                 
if (self._settings.autoSubmit) {
409                     self.submit();
410                 }
411             });
412
413             addEvent(input,
'mouseover', function(){
414                 addClass(self._button, self._settings.hoverClass);
415             });
416             
417             addEvent(input,
'mouseout', function(){
418                 removeClass(self._button, self._settings.hoverClass);
419                 
420                 
// We use visibility instead of display to fix problem with Safari 4
421                 
// The problem is that the value of input doesn't change if it
422                 
// has display none when user selects a file
423                 input.parentNode.style.visibility =
'hidden';
424
425             });
426                         
427             div.appendChild(input);
428             document.body.appendChild(div);
429               
430             
this._input = input;
431         },
432         _clearInput : function(){
433             
if (!this._input){
434                 
return;
435             }
436                              
437             
// this._input.value = ''; Doesn't work in IE6
438             removeNode(
this._input.parentNode);
439             
this._input = null;
440             
this._createInput();
441             
442             removeClass(
this._button, this._settings.hoverClass);
443         },
444         
/**
445          * Function makes sure that
when user clicks upload button,
446          * the
this._input is clicked instead
447          */

448         _rerouteClicks: function(){
449             
var self = this;
450             
451             
// IE will later display 'access denied' error
452             
// if you use using self._input.click()
453             
// other browsers just ignore click()
454
455             addEvent(self._button,
'mouseover', function(){
456                 
if (self._disabled){
457                     
return;
458                 }
459                                 
460                 
if ( ! self._input){
461                     self._createInput();
462                 }
463                 
464                 
var div = self._input.parentNode;
465                 copyLayout(self._button, div);
466                 div.style.visibility =
'visible';
467                                 
468             });
469             
470             
471             
// commented because we now hide input on mouseleave
472             
/**
473              * When the window
is resized the elements
474              * can be misaligned
if button position depends
475              *
on window size
476              */

477             
//addResizeEvent(function(){
478             
// if (self._input){
479             
// copyLayout(self._button, self._input.parentNode);
480             
// }
481             
//});
482                                          
483         },
484         
/**
485          * Creates iframe with unique name
486          * @
return {Element} iframe
487          */

488         _createIframe: function(){
489             
// We can't use getTime, because it sometimes return
490             
// same value in safari :(
491             
var id = getUID();
492              
493             
// We can't use following code as the name attribute
494             
// won't be properly registered in IE6, and new window
495             
// on form submit will open
496             
// var iframe = document.createElement('iframe');
497             
// iframe.setAttribute('name', id);
498  
499             
var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
500             
// src="javascript:false; was added
501             
// because it possibly removes ie6 prompt
502             
// "This page contains both secure and nonsecure items"
503             
// Anyway, it doesn't do any harm.
504             iframe.setAttribute('id', id);
505             
506             iframe.style.display = 'none';
507             document.body.appendChild(iframe);
508             
509             
return iframe;
510         },
511         
/**
512          * Creates form, that will be submitted to iframe
513          * @param {Element} iframe Where to submit
514          * @
return {Element} form
515          */

516         _createForm: function(iframe){
517             
var settings = this._settings;
518                         
519             
// We can't use the following code in IE6
520             
// var form = document.createElement('form');
521             
// form.setAttribute('method', 'post');
522             
// form.setAttribute('enctype', 'multipart/form-data');
523             
// Because in this case file won't be attached to request
524             
var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
525                         
526             form.setAttribute('action', settings.action);
527             form.setAttribute('target', iframe.name);
528             form.style.display = 'none';
529             document.body.appendChild(form);
530             
531             
// Create hidden input element for each data key
532             
for (var prop in settings.data) {
533                 
if (settings.data.hasOwnProperty(prop)){
534                     
var el = document.createElement("input");
535                     el.setAttribute('type', 'hidden');
536                     el.setAttribute('name', prop);
537                     el.setAttribute('
value', settings.data[prop]);
538                     form.appendChild(el);
539                 }
540             }
541             
return form;
542         },
543         
/**
544          * Gets response
from iframe and fires onComplete event when ready
545          * @param iframe
546          * @param file Filename to use
in onComplete callback
547          */

548         _getResponse : function(iframe, file){
549             
// getting response
550             
var toDeleteFlag = false, self = this, settings = this._settings;
551                
552             addEvent(iframe, 'load', function(){
553                 
554                 
if (// For Safari
555                     iframe.src == "
javascript:'%3Chtml%3E%3C/html%3E';" ||
556                     
// For FF, IE
557                     iframe.src == "
javascript:'<html></html>';"){
558                         
// First time around, do not delete.
559                         
// We reload to blank page, so that reloading main page
560                         
// does not re-submit the post.
561                         
562                         
if (toDeleteFlag) {
563                             
// Fix busy state in FF3
564                             setTimeout(function(){
565                                 removeNode(iframe);
566                             },
0);
567                         }
568                                                 
569                         
return;
570                 }
571                 
572                 
var doc = iframe.contentDocument ? iframe.contentDocument : window.frames[iframe.id].document;
573                 
574                 
// fixing Opera 9.26,10.00
575                 
if (doc.readyState && doc.readyState != 'complete') {
576                    
// Opera fires load event multiple times
577                    
// Even when the DOM is not ready yet
578                    
// this fix should not affect other browsers
579                    
return;
580                 }
581                 
582                 
// fixing Opera 9.64
583                 
if (doc.body && doc.body.innerHTML == "false") {
584                     
// In Opera 9.64 event was fired second time
585                     
// when body.innerHTML changed from false
586                     
// to server response approx. after 1 sec
587                     
return;
588                 }
589                 
590                 
var response;
591                 
592                 
if (doc.XMLDocument) {
593                     
// response is a xml document Internet Explorer property
594                     response = doc.XMLDocument;
595                 }
else if (doc.body){
596                     
// response is html document or plain text
597                     response = doc.body.innerHTML;
598                     
599                     
if (settings.responseType && settings.responseType.toLowerCase() == 'json') {
600                         
// If the document was sent as 'application/javascript' or
601                         
// 'text/javascript', then the browser wraps the text in a <pre>
602                         
// tag and performs html encoding on the contents. In this case,
603                         
// we need to pull the original text content from the text node's
604                         
// nodeValue property to retrieve the unmangled content.
605                         
// Note that IE6 only understands text/html
606                         
if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE') {
607                             response = doc.body.firstChild.firstChild.nodeValue;
608                         }
609                         
610                         
if (response) {
611                             response = eval("
(" + response + ")");
612                         }
else {
613                             response = {};
614                         }
615                     }
616                 }
else {
617                     
// response is a xml document
618                     response = doc;
619                 }
620                 
621                 settings.onComplete.call(self, file, response);
622                 
623                 
// Reload blank page, so that reloading main page
624                 
// does not re-submit the post. Also, remember to
625                 
// delete the frame
626                 toDeleteFlag =
true;
627                 
628                 
// Fix IE mixed content issue
629                 iframe.src = "
javascript:'<html></html>';";
630             });
631         },
632         
/**
633          * Upload file contained
in this._input
634          */

635         submit: function(){
636             
var self = this, settings = this._settings;
637             
638             
if ( ! this._input || this._input.value === ''){
639                 
return;
640             }
641                                     
642             
var file = fileFromPath(this._input.value);
643             
644             
// user returned false to cancel upload
645             
if (false === settings.onSubmit.call(this, file, getExt(file))){
646                 
this._clearInput();
647                 
return;
648             }
649             
650             
// sending request
651             
var iframe = this._createIframe();
652             
var form = this._createForm(iframe);
653             
654             
// assuming following structure
655             
// div -> input type='file'
656             removeNode(
this._input.parentNode);
657             removeClass(self._button, self._settings.hoverClass);
658                         
659             form.appendChild(
this._input);
660                         
661             form.submit();
662
663             
// request set, clean up
664             removeNode(form); form =
null;
665             removeNode(
this._input); this._input = null;
666             
667             
// Get response from iframe and fire onComplete event when ready
668             
this._getResponse(iframe, file);
669
670             
// get ready for next request
671             
this._createInput();
672         }
673     };
674 })();



Full source code website bán hàng thương mại điện tử gần giống shopee 472.082 lượt xem

Gõ tìm kiếm nhanh...